home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 …SCII & the Runetime Code / ADC Developer CD (1992-07) (''Butch ASCII And The Runtime Code'')_iso / Dev.CD 199207.iso / Developer Essentials / DTS Sample Code / Macintosh Sample Code / SC.013.OOPTESample / UTEDocument.inc1.p < prev    next >
Encoding:
Text File  |  1992-06-11  |  24.9 KB  |  837 lines  |  [TEXT/MPS ]

  1. {---------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple TextEdit Sample Application
  6. #
  7. #    OOPTESample
  8. #
  9. #    UTEDocument.inc1.p        -    Pascal Source
  10. #
  11. #    Copyright © 1988, 1989 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:        
  15. #                    1.00                    04/89
  16. #                    1.10                    02/90
  17. #                    1.11                    10/92
  18. #
  19. #    Components:     
  20. #                    BuildOOPTESample            February 1, 1990
  21. #                    MTESample.p                    February 1, 1990
  22. #                    OOPTESample.make            February 1, 1990
  23. #                    TECommon.h                    February 1, 1990
  24. #                    TESampleGlue.a                February 1, 1990
  25. #                    TESample.r                    February 1, 1990
  26. #                    TMLRules.make                February 1, 1990
  27. #                    UApplication.p                February 1, 1990
  28. #                    UApplication.inc1.p            February 1, 1990
  29. #                    UDocument.p                    February 1, 1990
  30. #                    UDocument.inc1.p            February 1, 1990
  31. #                    UTEDocument.p                February 1, 1990
  32. #                    UTEDocument.inc1.p            February 1, 1990
  33. #                    UTESample.p                    February 1, 1990
  34. #                    UTESample.inc1.p            February 1, 1990
  35. #
  36. ---------------------------------------------------------------------}
  37.  
  38. CONST
  39.     kTextMargin                = 2;    {kTextMargin is the number of pixels we leave
  40.                                     blank at the edge of the window.}
  41.  
  42.     kMaxDocWidth            = 576;    {kMaxDocWidth is an arbitrary number used to specify
  43.                                     the width of the TERec's destination rectangle so that
  44.                                     word wrap and horizontal scrolling can be demonstrated.}
  45.  
  46.  
  47.     kMinDocDim                = 64;    {kMinDocDim is used to limit the minimum dimension
  48.                                     of a window when GrowWindow is called.}
  49.  
  50.  
  51.     kControlInvisible        = 0;
  52.     kControlVisible            = $FF;    {kControlInvisible is used to 'turn off' controls
  53.                                     (i.e., cause the control not to be redrawn as a result
  54.                                     of some Control Manager call such as SetCtlValue)
  55.                                     by being put into the contrlVis field of the record.
  56.                                     kControlVisible is used the same way to 'turn on'
  57.                                     the control.}
  58.  
  59.     kScrollbarWidth            = 16;    {kScrollBarAdjust and kScrollBarWidth are used in
  60.                                     calculating values for control positioning and sizing.}
  61.     kScrollbarAdjust        = kScrollbarWidth - 1;
  62.     kGrowboxAdjust             = 15;
  63.  
  64.     kScrollTweek            = 2;    {kScrollTweek compensates for off-by-one requirements
  65.                                     of the scrollbars to have borders coincide with the
  66.                                     growbox.}
  67.  
  68.     kCRChar                    = chr(13);
  69.     kDelChar                = chr(8);    {kCrChar is used to match with a carriage return
  70.                                     when calculating the number of lines in the TextEdit
  71.                                     record. kDelChar is used to check for delete in
  72.                                     keyDowns.}
  73.  
  74.     kButtonScroll            = 4;    {kButtonScroll is how many pixels to scroll horizontally
  75.                                     when the button part of the horizontal scrollbar is
  76.                                     pressed.}
  77.  
  78.  
  79.     kMaxTELength            = 32000;    {kMaxTELength is an arbitrary number used to limit
  80.                                     the length of text in the TERec so that various errors
  81.                                     won't occur from too many characters in the text.}
  82.  
  83.     kTESlop                    = 1024;    { provides some extra security when pre-flighting
  84.                                     edit commands. }
  85.  
  86.     rVScroll                = 128;    { vertical scrollbar control }
  87.     rHScroll                = 129;    { horizontal scrollbar control }
  88.  
  89.     kTEDocErrStrings         = 129;    { id of our STR# for error strings }
  90.  
  91.     { The following are indicies into STR# resources. }
  92.     eNoMemory                = 1;
  93.     eNoSpaceCut                = 2;
  94.     eNoCut                    = 3;
  95.     eNoCopy                    = 4;
  96.     eExceedPaste            = 5;
  97.     eNoSpacePaste            = 6;
  98.     eNoWindow                = 7;
  99.     eExceedChar                = 8;
  100.     eNoPaste                = 9;
  101.  
  102. (********************************************************************************************)
  103. (*        G l o b a l   R o u t i n e s                                                        *)
  104. (********************************************************************************************)
  105. (*
  106.     Routines used by this class, which don't belong to the class since we use
  107.     them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
  108. *)
  109.  
  110.  
  111. PROCEDURE AsmClikLoop; EXTERNAL;
  112.  
  113. { Common algorithm for pinning the value of a control. It returns the actual amount }
  114. { the value of the control changed. }
  115.  
  116. {$S Main}
  117. PROCEDURE CommonAction(control:ControlHandle; VAR amount:integer);
  118. VAR
  119.     value, max: integer;
  120. BEGIN
  121.     value := GetCtlValue(control);
  122.     max := GetCtlMax(control);
  123.     amount := value - amount;
  124.     IF (amount <= 0) THEN
  125.         amount := 0
  126.     ELSE IF (amount >= max) THEN
  127.         amount := max;
  128.     SetCtlValue(control, amount);
  129.     amount := value - amount;   { calculate true change }
  130. END; { CommonAction  }
  131.  
  132.  
  133. { Determines how much to change the value of the vertical scrollbar by and how }
  134. { much to scroll the TE record.}
  135.  
  136. {$S Main}
  137. PROCEDURE VActionProc(control:ControlHandle;part:integer);
  138. VAR
  139.     amount:integer;
  140.     window:WindowPtr;
  141.     hTE:TEHandle;
  142.     doc:TTEDocument;
  143. BEGIN
  144.     IF (part <> 0) THEN BEGIN
  145.         window := control^^.contrlOwner;
  146.         doc := TTEDocument(gApplication.DocList.FindDoc(window));
  147.         hTE := doc.GetTEHandle;
  148.         CASE part OF
  149.             inUpButton, inDownButton:        { one line  }
  150.                 amount := 1;
  151.             inPageUp, inPageDown:            { one page  }
  152.                 WITH hTE^^,viewRect DO
  153.                     amount := (bottom - top) DIV lineHeight;
  154.         END;
  155.         IF ((part = inDownButton) OR (part = inPageDown)) THEN
  156.             amount := -amount;        { reverse direction for a downer  }
  157.         CommonAction(control, amount);
  158.         IF (amount <> 0) THEN
  159.             TEScroll(0, amount*hTE^^.lineHeight, hTE);
  160.     END;
  161. END; { VActionProc }
  162.  
  163. { Determines how much to change the value of the horizontal scrollbar by and how }
  164. { much to scroll the TE record. }
  165.  
  166. {$S Main}
  167. PROCEDURE HActionProc(control:ControlHandle;part:integer);
  168. VAR
  169.     amount:integer;
  170.     window:WindowPtr;
  171.     hTE:TEHandle;
  172.     doc:TTEDocument;
  173. BEGIN
  174.     IF (part <> 0) THEN BEGIN
  175.         window := control^^.contrlOwner;
  176.         doc := TTEDocument(gApplication.DocList.FindDoc(window));
  177.         hTE := doc.GetTEHandle;
  178.         CASE part OF
  179.             inUpButton, inDownButton:        { a few pixels }
  180.                 amount := kButtonScroll;
  181.             inPageUp, inPageDown:            { a page width }
  182.                 WITH hTE^^.viewRect DO
  183.                     amount := (right - left);
  184.         END;
  185.         IF ((part = inDownButton) OR (part = inPageDown)) THEN
  186.             amount := -amount;        { reverse direction }
  187.         CommonAction(control, amount);
  188.         IF (amount <> 0) THEN
  189.             TEScroll(amount, 0, hTE);
  190.     END;
  191. END; { HActionProc }
  192.  
  193. { Gets called from our assembly language routine, AsmClikLoop, which in turn }
  194. { is called by the TEClick toolbox routine. Saves the window's clip region, }
  195. { sets it to the portRect, adjusts the scrollbar values to match the TE scroll }
  196. { amount, then restores the clip region. }
  197.  
  198. {$S Main}
  199. PROCEDURE PascalClikLoop;
  200. VAR
  201.     region: RgnHandle;
  202.     wind: WindowPtr;
  203.     doc: TTEDocument;
  204. BEGIN
  205.     wind := FrontWindow;
  206.     doc := TTEDocument(gApplication.DocList.FindDoc(wind));
  207.     region := NewRgn;
  208.     GetClip(region);                { save the old clip }
  209.     ClipRect(wind^.portRect);        { set the new clip }
  210.     doc.AdjustScrollValues(FALSE);    { pass false for canRedraw }
  211.     SetClip(region);                { restore the old clip }
  212.     DisposeRgn(region);
  213. END; { PascalClikLoop }
  214.  
  215. { Gets called from our assembly language routine, AsmClikLoop, which is in }
  216. { turn called by the TEClick toolbox routine. It returns the address of the }
  217. { default clikLoop routine that was put into the TERec by TEAutoView to }
  218. { AsmClikLoop so that it can call it. }
  219.  
  220. {$S Main}
  221. FUNCTION GetOldClikLoop:ProcPtr;
  222. VAR
  223.     doc: TTEDocument;
  224. BEGIN
  225.     doc := TTEDocument(gApplication.DocList.FindDoc(FrontWindow));
  226.     IF (doc = NIL) THEN
  227.         GetOldClikLoop := nil
  228.     ELSE
  229.         GetOldClikLoop := doc.GetClikLoop;
  230. END; { GetOldClikLoop }
  231.  
  232.  
  233. (********************************************************************************************)
  234. (*        T T E D o c u m e n t                                                                *)
  235. (********************************************************************************************)
  236.  
  237. {$S Initialize}
  238. {-----------------------------------+
  239. |    ITEDocument                        |
  240. +-----------------------------------}
  241. PROCEDURE TTEDocument.ITEDocument(resID:integer);
  242. VAR
  243.     good:Boolean;
  244.     destRect, viewRect: Rect;
  245. BEGIN
  246.  
  247.     IDocument(resID);
  248.  
  249.     SetPort(fDocWindow);
  250.     GetTERect(viewRect);
  251.     destRect := viewRect;
  252.     destRect.right := destRect.left + kMaxDocWidth;
  253.     fDocTE := TENew(destRect, viewRect);
  254.  
  255.     good := (fDocTE <> NIL);        { if TENew succeeded, we have a good document. }
  256.     IF good THEN BEGIN                { good document? — get scrollbars  }
  257.         AdjustViewRect;
  258.         TEAutoView(TRUE, fDocTE);
  259.         fDocClik := fDocTE^^.clikLoop;
  260.         fDocTE^^.clikLoop := @AsmClikLoop;
  261.         fDocVScroll := GetNewControl(rVScroll, fDocWindow);
  262.         good := (fDocVScroll <> NIL);
  263.     END;
  264.     IF good THEN BEGIN
  265.         fDocHScroll := GetNewControl(rHScroll, fDocWindow);
  266.         good := (fDocHScroll <> NIL);
  267.     END;
  268.  
  269.     IF good THEN BEGIN                { good? — adjust & draw the controls, draw the window }
  270.         AdjustScrollValues(FALSE);
  271.         ShowWindow(fDocWindow);
  272.     END ELSE BEGIN                    { tell user we failed }
  273.         AlertUser(kTEDocErrStrings,eNoWindow);
  274.     END;
  275. END;
  276.  
  277. {$S Main}
  278. {-----------------------------------+
  279. |    Free                            |
  280. +-----------------------------------}
  281. PROCEDURE TTEDocument.Free; OVERRIDE;
  282. BEGIN
  283.     HideWindow(fDocWindow);
  284.     IF fDocTE <> NIL THEN
  285.       TEDispose(fDocTE);            { dispose the TEHandle if we got far enough to make one  }
  286.     IF fDocVScroll <> NIL THEN
  287.       DisposeControl(fDocVScroll);
  288.     IF fDocHScroll <> NIL THEN
  289.       DisposeControl(fDocHScroll);
  290.     INHERITED Free;
  291. END;
  292.  
  293. {$S Main}
  294. {-----------------------------------+
  295. |    DoZoom                            |
  296. +-----------------------------------}
  297. PROCEDURE TTEDocument.DoZoom(partCode:integer); OVERRIDE;
  298. BEGIN
  299.     EraseRect(fDocWindow^.portRect);
  300.     ZoomWindow(fDocWindow, partCode, (fDocWindow = FrontWindow));
  301.     ResizeWindow;    {after this, only thing valid is scrollbars.}
  302. END;
  303.  
  304. {$S Main}
  305. {-----------------------------------+
  306. |    DoGrow                            |
  307. +-----------------------------------}
  308.  
  309. {Called when a mouseDown occurs in the grow box of an active window. In
  310.  order to eliminate any 'flicker', we want to invalidate only what is
  311.  necessary. Since ResizeWindow invalidates the whole portRect, we save
  312.  the old TE viewRect, intersect it with the new TE viewRect, and
  313.  remove the result from the update region. However, we must make sure
  314.  that any old update region that might have been around gets put back.}
  315.  
  316. PROCEDURE TTEDocument.DoGrow(theEvent:EventRecord); OVERRIDE;
  317. VAR
  318.     growResult: longint;
  319.     tempRect:    Rect;
  320.     tempRgn:    RgnHandle;
  321.     
  322.     PROCEDURE GetLocalUpdateRgn(aRgn:RgnHandle);
  323.     BEGIN
  324.         CopyRgn(WindowPeek(fDocWindow)^.updateRgn, aRgn);    {save old update region}
  325.         WITH fDocWindow^.portBits.bounds DO
  326.             OffsetRgn(aRgn, left, top);                        {convert to local coords}
  327.     END;
  328.  
  329. BEGIN
  330.     tempRect := screenBits.bounds;
  331.     tempRect.left := kMinDocDim;
  332.     tempRect.top := kMinDocDim;
  333.     growResult := GrowWindow(fDocWindow, theEvent.where, tempRect);
  334.     { see if it really changed size  }
  335.     IF growResult <> 0 THEN BEGIN
  336.         tempRect := fDocTE^^.viewRect;
  337.         tempRgn := NewRgn;
  338.         GetLocalUpdateRgn(tempRgn);
  339.         SizeWindow(fDocWindow, LoWrd(growResult), HiWrd(growResult), TRUE);
  340.         ResizeWindow;    {after this, only thing valid is scrollbars.}
  341.         IF SectRect(tempRect, fDocTE^^.viewRect, tempRect) THEN;
  342.         ValidRect(tempRect);
  343.         InvalRgn(tempRgn);
  344.         DisposeRgn(tempRgn);
  345.     END;
  346. END;
  347.  
  348. {$S Main}
  349. {-----------------------------------+
  350. |    DoContent                        |
  351. +-----------------------------------}
  352. PROCEDURE TTEDocument.DoContent(theEvent:EventRecord); OVERRIDE;
  353. VAR
  354.     mouse:        Point;
  355.     control:    ControlHandle;
  356.     part,value:    integer;
  357.     shiftDown:    Boolean;
  358. BEGIN
  359.     SetPort(fDocWindow);
  360.     mouse := theEvent.where;            { get the click position  }
  361.     GlobalToLocal(mouse);
  362.     part := FindControl(mouse, fDocWindow, control);
  363.     CASE part OF
  364.           0: BEGIN                        { not in a control }
  365.             { see if we need to extend the selection  }
  366.             shiftDown := BAnd(theEvent.modifiers, shiftKey) <> 0;    { extend if Shift is down  }
  367.             TEClick(mouse, shiftDown, fDocTE);
  368.         END;
  369.         inThumb: BEGIN
  370.             value := GetCtlValue(control);
  371.             part := TrackControl(control, mouse, NIL);
  372.             IF part <> 0 THEN BEGIN
  373.                 value := value - GetCtlValue(control);
  374.                 { value now has CHANGE in value; if value changed, scroll  }
  375.                 IF value <> 0 THEN BEGIN
  376.                     IF control = fDocVScroll THEN BEGIN
  377.                         TEScroll(0, value * fDocTE^^.lineHeight, fDocTE);
  378.                     END ELSE BEGIN
  379.                         TEScroll(value, 0, fDocTE);
  380.                     END;
  381.                 END;
  382.             END;
  383.         END;
  384.         OTHERWISE BEGIN                        { they clicked in an arrow, so track & scroll  }
  385.             IF control = fDocVScroll THEN BEGIN
  386.                 value := TrackControl(control, mouse, @VActionProc);
  387.             END ELSE BEGIN
  388.                 value := TrackControl(control, mouse, @HActionProc);
  389.             END;
  390.         END;
  391.     END;
  392. END;
  393.  
  394. {$S Main}
  395. {-----------------------------------+
  396. |    DoKeyDown                        |
  397. +-----------------------------------}
  398. PROCEDURE TTEDocument.DoKeyDown(theEvent:EventRecord); OVERRIDE;
  399. VAR
  400.     key: char;
  401. BEGIN
  402.     IF BAnd(theEvent.modifiers, cmdKey) <> 1 THEN BEGIN    { don't process command characters }
  403.         key := char(BAnd(theEvent.message, charCodeMask));
  404.         { we have a char. for our window; see if we are still below TextEdit’s }
  405.         { limit for the number of characters }
  406.         WITH fdocTE^^ DO BEGIN
  407.             IF ((key = kDelChar) OR ((teLength - selEnd - selStart) + 1 < kMaxTELength)) THEN BEGIN
  408.                 TEKey(key, fDocTE);
  409.                 AdjustScrollbars(FALSE);
  410.                 AdjustTE;
  411.             END ELSE BEGIN
  412.                 AlertUser(kTEDocErrStrings,eExceedChar);
  413.             END;
  414.         END;
  415.     END;
  416. END;
  417.  
  418. {$S Main}
  419. {-----------------------------------+
  420. |    DoActivate                        |
  421. +-----------------------------------}
  422. PROCEDURE TTEDocument.DoActivate(becomingActive:Boolean); OVERRIDE;
  423. VAR
  424.     tempRgn:    RgnHandle;
  425.     clipRgn:    RgnHandle;
  426.     growRect:    Rect;
  427. BEGIN
  428.     IF becomingActive THEN BEGIN
  429.  
  430.         { since we don’t want TEActivate to draw a selection in an area where }
  431.         { we’re going to erase and redraw, we’ll clip out the update region }
  432.         { before calling it. }
  433.         tempRgn := NewRgn;
  434.         clipRgn := NewRgn;
  435.         { save old update region }
  436.         CopyRgn(WindowPeek(fDocWindow)^.updateRgn, tempRgn);
  437.         { put it in local coords }
  438.         WITH fDocWindow^.portBits.bounds DO
  439.             OffsetRgn(tempRgn, left, top);
  440.         GetClip(clipRgn);
  441.         { subtract updateRgn from clipRgn }
  442.         DiffRgn(clipRgn, tempRgn, tempRgn);
  443.         { make it the new clipRgn }
  444.         SetClip(tempRgn);
  445.         TEActivate(fDocTE);
  446.         { restore the full-blown clipRgn }
  447.         SetClip(clipRgn);
  448.         { get rid of temp regions }
  449.         DisposeRgn(tempRgn);
  450.         DisposeRgn(clipRgn);
  451.  
  452.         { the controls must be redrawn on activation: }
  453.         fDocVScroll^^.contrlVis := kControlVisible;
  454.         fDocHScroll^^.contrlVis := kControlVisible;
  455.         InvalRect(fDocVScroll^^.contrlRect);
  456.         InvalRect(fDocHScroll^^.contrlRect);
  457.         { the growbox needs to be redrawn on activation: }
  458.         growRect := fDocWindow^.portRect;
  459.         { adjust for the scrollbars }
  460.         WITH growRect DO BEGIN
  461.             top := bottom - kScrollbarAdjust;
  462.             left := right - kScrollbarAdjust;
  463.         END;
  464.         InvalRect(growRect);
  465.     END ELSE BEGIN                { becoming Inactive }
  466.         TEDeactivate(fDocTE);
  467.         { the controls must be hidden on deactivation: }
  468.         HideControl(fDocVScroll);
  469.         HideControl(fDocHScroll);
  470.         { we draw grow icon immediately, since we deactivate controls }
  471.         { immediately, and the update delay looks funny }
  472.         DrawGrowIcon(fDocWindow);
  473.     END;
  474. END;
  475.  
  476. {$S Main}
  477. {-----------------------------------+
  478. |    DoIdle                            |
  479. +-----------------------------------}
  480. PROCEDURE TTEDocument.DoIdle; OVERRIDE;
  481. BEGIN
  482.     TEIdle(fDocTE);
  483. END;
  484.  
  485. {$S Main}
  486. {-----------------------------------+
  487. |    DoUpdate                        |
  488. +-----------------------------------}
  489. PROCEDURE TTEDocument.DoUpdate; OVERRIDE;
  490. BEGIN
  491.     BeginUpdate(fDocWindow);                    { this sets up the visRgn }
  492.     IF NOT EmptyRgn(fDocWindow^.visRgn) THEN    { draw if updating needs to be done }
  493.         DrawWindow;
  494.     EndUpdate(fDocWindow);
  495. END;
  496.  
  497. {$S Main}
  498. {-----------------------------------+
  499. |    DoCut                            |
  500. +-----------------------------------}
  501. PROCEDURE TTEDocument.DoCut; OVERRIDE;
  502. VAR
  503.     total, contig:    longint;
  504. BEGIN
  505.     IF (ZeroScrap = noErr) THEN BEGIN
  506.         PurgeSpace(total, contig);
  507.         IF (fDocTE^^.selEnd - fDocTE^^.selStart + kTESlop > contig) THEN BEGIN
  508.             AlertUser(kTEDocErrStrings,eNoSpaceCut);
  509.         END ELSE BEGIN
  510.             TECut(fDocTE);
  511.             IF (TEToScrap <> noErr) THEN BEGIN
  512.                 AlertUser(kTEDocErrStrings,eNoCut);
  513.                 IF Boolean(ZeroScrap) THEN;
  514.             END;
  515.         END;
  516.     END;
  517.     AdjustScrollbars(FALSE);
  518.     AdjustTE;
  519. END;
  520.  
  521. {$S Main}
  522. {-----------------------------------+
  523. |    DoCopy                            |
  524. +-----------------------------------}
  525. PROCEDURE TTEDocument.DoCopy; OVERRIDE;
  526. BEGIN
  527.     IF (ZeroScrap = noErr) THEN BEGIN
  528.         TECopy(fDocTE);                { after copying, export the TE scrap }
  529.         IF (TEToScrap <> noErr) THEN BEGIN
  530.             AlertUser(kTEDocErrStrings,eNoCopy);
  531.             IF Boolean(ZeroScrap) THEN;
  532.         END;
  533.     END;
  534.     AdjustScrollbars(FALSE);
  535.     AdjustTE;
  536. END;
  537.  
  538. {$S Main}
  539. {-----------------------------------+
  540. |    DoPaste                            |
  541. +-----------------------------------}
  542. PROCEDURE TTEDocument.DoPaste; OVERRIDE;
  543. VAR
  544.     aHandle:    Handle;
  545.     oldsize,
  546.     newSize:    longint;
  547.     saveErr:    OSErr;
  548. BEGIN
  549.     IF (TEFromScrap = noErr) THEN BEGIN
  550.         WITH fDocTE^^ DO BEGIN
  551.             IF (TEGetScrapLen + (teLength - (selEnd - selStart)) > kMaxTELength) THEN BEGIN
  552.                 AlertUser(kTEDocErrStrings,eExceedPaste);
  553.             END ELSE BEGIN
  554.                 aHandle := Handle(TEGetText(fDocTE));
  555.                 oldSize := GetHandleSize(aHandle);
  556.                 newSize := oldSize + TEGetScrapLen + kTESlop;
  557.                 SetHandleSize(aHandle, newSize);
  558.                 saveErr := MemError;
  559.                 SetHandleSize(aHandle, oldSize);
  560.                 IF (saveErr <> noErr) THEN BEGIN
  561.                     AlertUser(kTEDocErrStrings,eNoSpacePaste);
  562.                 END ELSE BEGIN
  563.                     TEPaste(fDocTE);
  564.                 END;
  565.             END;
  566.         END;
  567.     END ELSE BEGIN
  568.         AlertUser(kTEDocErrStrings,eNoPaste);
  569.     END;
  570.     AdjustScrollbars(FALSE);
  571.     AdjustTE;
  572. END;
  573.  
  574. {$S Main}
  575. {-----------------------------------+
  576. |    DoClear                            |
  577. +-----------------------------------}
  578. PROCEDURE TTEDocument.DoClear; OVERRIDE;
  579. BEGIN
  580.     TEDelete(fDocTE);
  581.     AdjustScrollbars(FALSE);
  582.     AdjustTE;
  583. END;
  584.  
  585. {$S Main}
  586. {-----------------------------------+
  587. |    HaveSelection                    |
  588. +-----------------------------------}
  589. FUNCTION TTEDocument.HaveSelection:Boolean; OVERRIDE;
  590. BEGIN
  591.     IF (fDocTE^^.selStart < fDocTE^^.selEnd) THEN
  592.         HaveSelection := TRUE
  593.     ELSE
  594.         HaveSelection := FALSE;
  595. END;
  596.  
  597. {$S Main}
  598. {-----------------------------------+
  599. |    CalcIdle                        |
  600. +-----------------------------------}
  601. FUNCTION TTEDocument.CalcIdle:Longint; OVERRIDE;
  602. BEGIN
  603.     IF NOT(HaveSelection) THEN BEGIN
  604.         CalcIdle := GetCaretTime;
  605.     END ELSE BEGIN
  606.         CalcIdle := $7FFFFFFF;
  607.     END;
  608. END;
  609.  
  610. {$S Main}
  611. {-----------------------------------+
  612. |    AdjustScrollValues                |
  613. +-----------------------------------}
  614. { Simply call the common adjust routine for the vertical and horizontal scrollbars. }
  615. PROCEDURE TTEDocument.AdjustScrollValues(mustRedraw:Boolean);
  616. BEGIN
  617.     AdjustHV(true, mustRedraw);
  618.     AdjustHV(false, mustRedraw);
  619. END;
  620.  
  621. {$S Main}
  622. {-----------------------------------+
  623. |    GetClikLoop                        |
  624. +-----------------------------------}
  625. FUNCTION TTEDocument.GetClikLoop:ProcPtr;
  626. BEGIN
  627.     GetClikLoop := fDocClik;
  628. END;
  629.  
  630. {$S Main}
  631. {-----------------------------------+
  632. |    GetTEHandle                        |
  633. +-----------------------------------}
  634. FUNCTION TTEDocument.GetTEHandle:TEHandle;
  635. BEGIN
  636.     GetTEHandle := fDocTE;
  637. END;
  638.  
  639. {$S Main}
  640. {-----------------------------------+
  641. |    GetVisTERgn                        |
  642. +-----------------------------------}
  643. PROCEDURE TTEDocument.GetVisTERgn(rgn:RgnHandle);
  644. VAR
  645.     teRect:    Rect;
  646. BEGIN
  647.     teRect := fDocTE^^.viewRect;        { get a local copy of viewRect }
  648.     SetPort(fDocWindow);            { make sure we have right port }
  649.     LocalToGlobal(teRect.topLeft);
  650.     LocalToGlobal(teRect.botRight);
  651.     RectRgn(rgn, teRect);
  652.     { we temporarily change the port’s origin to “globalfy” the visRgn }
  653.     WITH fDocWindow^.portbits.bounds DO
  654.         SetOrigin(-left,-top);
  655.     SectRgn(rgn, fDocWindow^.visRgn, rgn);
  656.     SetOrigin(0, 0);
  657. END;
  658.  
  659. {$S Main}
  660. {-----------------------------------+
  661. |    GetTERect                        |
  662. +-----------------------------------}
  663. { Return a rectangle that is inset from the portRect by the size of }
  664. { the scrollbars and a little extra margin. }
  665. PROCEDURE TTEDocument.GetTERect(VAR teRect:Rect);
  666. BEGIN
  667.     teRect := fDocWindow^.portRect;
  668.     InsetRect(teRect, kTextMargin, kTextMargin);        { adjust for margin  }
  669.     WITH teRect DO BEGIN
  670.         bottom := bottom - kScrollbarAdjust;            { and for the scrollbars }
  671.         right := right - kScrollbarAdjust;
  672.     END;
  673. END;
  674.  
  675. {$S Main}
  676. {-----------------------------------+
  677. |    AdjustTE                        |
  678. +-----------------------------------}
  679. { Scroll the TERec around to match up to the potentially updated scrollbar }
  680. { values. This is really useful when the window has been resized such that the }
  681. { scrollbars became inactive but the TERec was already scrolled. }
  682. PROCEDURE TTEDocument.AdjustTE;
  683. BEGIN
  684.     WITH fDocTE^^ DO BEGIN
  685.         TEScroll((viewRect.left - destRect.left) - GetCtlValue(fDocHScroll),
  686.                  (viewRect.top - destRect.top) - (GetCtlValue(fDocVScroll) * lineHeight),
  687.                  fDocTE);
  688.     END;
  689. END;
  690.  
  691. {$S Main}
  692. {-----------------------------------+
  693. |    DrawWindow                        |
  694. +-----------------------------------}
  695. PROCEDURE TTEDocument.DrawWindow;
  696. BEGIN
  697.     SetPort(fDocWindow);
  698.     EraseRect(fDocWindow^.portRect);    { As per TextEdit chapter of Inside Macintosh }
  699.     DrawControls(fDocWindow);            { This ordering makes for a better appearance }
  700.     DrawGrowIcon(fDocWindow);
  701.     TEUpdate(fDocWindow^.portRect, fDocTE);
  702. END;
  703.  
  704. {$S Main}
  705. {-----------------------------------+
  706. |    AdjustViewRect                    |
  707. +-----------------------------------}
  708. { Update the TERec's view rect so that it is the greatest multiple of }
  709. { the lineHeight that still fits in the old viewRect. }
  710. PROCEDURE TTEDocument.AdjustViewRect;
  711. BEGIN
  712.     WITH fDocTE^^,fDocTE^^.viewRect DO BEGIN
  713.         bottom := (((bottom - top) DIV lineHeight) * lineHeight) + top;
  714.     END;
  715. END;
  716.  
  717. {$S Main}
  718. {-----------------------------------+
  719. |    ResizeWindow                    |
  720. +-----------------------------------}
  721.  
  722. {Called when the window has been resized to fix up the controls and content.
  723.  This routine moves and resizes the scrollbars, adjusts their values, scrolls
  724.  the TERecord in case we grew the window out from under it, invalidates the
  725.  entire port, and then validates the areas under the scrollbars.}
  726.  
  727. PROCEDURE TTEDocument.ResizeWindow;
  728. BEGIN
  729.     AdjustScrollbars(TRUE);                { adjust, redraw anyway  }
  730.     AdjustTE;
  731.     InvalRect(fDocWindow^.portRect);    { invalidate the whole content  }
  732.     { the scrollbars were taken care of by AdjustScrollbars, so validate ’em  }
  733.     ValidRect(fDocVScroll^^.contrlRect);
  734.     ValidRect(fDocHScroll^^.contrlRect);
  735. END;
  736.  
  737. {$S Main}
  738. {-----------------------------------+
  739. |    AdjustHV                        |
  740. +-----------------------------------}
  741. { Calculate the new control maximum value and current value, whether it is the horizontal or }
  742. { vertical scrollbar. The vertical max is calculated by comparing the number of lines to the }
  743. { vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document }
  744. { width to the width of the viewRect. The current values are set by comparing the offset between }
  745. { the view and destination rects. If necessary, redraw the control by calling ShowControl. }
  746. PROCEDURE TTEDocument.AdjustHV(isVert, mustRedraw:Boolean);
  747. VAR
  748.     value, lines, max:    integer;
  749.     oldValue, oldMax:    integer;
  750.     hTE:                TEHandle;
  751.     control:            ControlHandle;
  752. BEGIN
  753.     IF isVert THEN
  754.         control := fDocVScroll
  755.     ELSE
  756.         control := fDocHScroll;
  757.     oldValue := GetCtlValue(control);
  758.     oldMax := GetCtlMax(control);
  759.     hTE := fDocTE;
  760.     WITH hTE^^ DO BEGIN
  761.         IF isVert THEN BEGIN
  762.             lines := nLines;
  763.             { since nLines isn’t right if the last character is a return, check for that case }
  764.             { MUST perform a short circuit check here, or we get a check error. }
  765.             IF ((teLength > 0) & (CharsHandle(hText)^^[teLength-1] = kCrChar)) THEN
  766.                 lines := lines + 1;
  767.             WITH viewRect DO max := lines - ((bottom - top) DIV lineHeight);
  768.         END    ELSE BEGIN
  769.             WITH viewRect DO max := kMaxDocWidth - (right - left);
  770.         END;
  771.     END;
  772.     IF max < 0 THEN max := 0;
  773.     SetCtlMax(control, max);
  774.     
  775.     WITH hTE^^ DO BEGIN
  776.         IF isVert THEN BEGIN
  777.             value := (viewRect.top - destRect.top) DIV lineHeight
  778.         END ELSE BEGIN
  779.             value := viewRect.left - destRect.left;
  780.         END;
  781.     END;
  782.     
  783.     {Pin the value to within range}
  784.     IF value < 0 THEN value := 0;
  785.     IF value > max THEN value := max;
  786.     
  787.     SetCtlValue(control, value);
  788.     { now redraw the control if asked to or if a setting changed  }
  789.     IF ((mustRedraw) OR ((max <> oldMax) OR (value <> oldValue))) THEN
  790.         ShowControl(control);
  791. END;
  792.  
  793. {$S Main}
  794. {-----------------------------------+
  795. |    AdjustScrollSizes                |
  796. +-----------------------------------}
  797. { Re-calculate the position and size of the viewRect and the scrollbars. }
  798. { kScrollTweek compensates for off-by-one requirements of the scrollbars }
  799. { to have borders coincide with the growbox. }
  800. PROCEDURE TTEDocument.AdjustScrollSizes;
  801. VAR
  802.     teRect:    Rect;
  803. BEGIN    
  804.     GetTERect(teRect);
  805.     fDocTE^^.viewRect := teRect;
  806.     AdjustViewRect;
  807.     WITH fDocWindow^.portRect DO BEGIN
  808.         MoveControl(fDocVScroll, right - kScrollbarAdjust, -1);
  809.         SizeControl(fDocVScroll, kScrollbarWidth, bottom - top - kGrowboxAdjust + kScrollTweek);
  810.         MoveControl(fDocHScroll, -1, bottom - kScrollbarAdjust);
  811.         SizeControl(fDocHScroll, right - left - kGrowboxAdjust + kScrollTweek, kScrollbarWidth);
  812.     END;
  813. END;
  814.  
  815. {$S Main}
  816. {-----------------------------------+
  817. |    AdjustScrollbars                |
  818. +-----------------------------------}
  819. { Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them }
  820. { and we don't want that). If the controls are to be resized as well, call the procedure to do that, }
  821. { then call the procedure to adjust the maximum and current values. Finally re-enable the controls }
  822. { by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be }
  823. { necessary). }
  824. PROCEDURE TTEDocument.AdjustScrollbars(needsResize:Boolean);
  825. BEGIN
  826.     { First, turn visibility of scrollbars off so we won’t get unwanted redrawing  }
  827.     fDocVScroll^^.contrlVis := kControlInvisible;
  828.     fDocHScroll^^.contrlVis := kControlInvisible;
  829.     IF needsResize THEN BEGIN
  830.         AdjustScrollSizes;
  831.     END;
  832.     AdjustScrollValues(needsResize);
  833.     { Now, restore visibility in case we never had to draw during adjustment  }
  834.     fDocVScroll^^.contrlVis := kControlVisible;
  835.     fDocHScroll^^.contrlVis := kControlVisible;
  836. END;
  837.